/**
 * ex13. 反射
 *
 * @author cyy
 * @date 2020/11/12
 */
package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)

type myFloat float64
type PC struct {
	Brand string  `json:"brand" form:"pc brand"`
	Price float64 `json:"price"`
}

func (pc *PC) SetInfo(brand string, price float64) {
	pc.Brand = brand
	pc.Price = price
}

func (pc PC) PrintInfo() {
	jsonByte, _ := json.Marshal(pc)
	fmt.Printf(string(jsonByte))
}

func main() {
	/*
		反射
			在程序运行期间对程序本身进行访问和修改的能力
			正常情况程序在编译时，变量被转换为内存地址，变量名不会被编译器写入到可执行部分；在运行程序时，程序无法获取自身的信息。
			支持反射的语言可以在程序编译器将变量的反射信息整合到可执行文件中，并给程序提供接口访问反射信息，这样就可以在程序运行期获取类型的反射信息，并且有能力修改他们。
	*/
	// 由reflect包提供对应的反射方法，结构体则打出对应结构体的type
	a := 1
	b := "str1"
	var i myFloat = 1
	pc := PC{}
	var ints []int
	printObjType(a)    // int		int
	printObjType(b)    // string	string
	printObjType(pc)   // PC		struct
	printObjType(i)    // myFloat	float
	printObjType(&i)   // 	 		ptr
	printObjType(ints) //	 		slice

	// 通过反射获取变量的值
	printObjVal(a)
	printObjVal("str")

	// 通过反射设置变量的值
	setObjVal(&a)
	fmt.Println(a) // 2
	setObjVal(&b)
	fmt.Println(b) // str2

	/*
		结构体的反射
	*/
	pc1 := PC{
		Brand: "联想",
		Price: 7999.99,
	}
	// name:	Brand, type:	string, tag:	json:"brand"
	// name:	Price, type:	float64, tag:	json:"price"
	PrintStructFields(pc1)
	PrintStructMethods(&pc1)
}

func printObjType(o interface{}) {
	/*
		TypeOf
			go中的类型分为type和kind，后者指底层的类型
	*/
	t := reflect.TypeOf(o)
	fmt.Println("Name:", t.Name())
	fmt.Println("Kind:", t.Kind())
}

func printObjVal(o interface{}) {
	/*
		ValueOf
			返回的是reflect.Value类型，与原始值之间可以相互转换
	*/
	v := reflect.ValueOf(o)
	k := v.Kind()
	switch k {
	case reflect.Int:
		fmt.Println(v.Int())
	case reflect.Float32:
		fmt.Println(v.Float())
	case reflect.Float64:
		fmt.Println(v.Float())
	case reflect.String:
		fmt.Println(v.String())
	default:
		fmt.Println("other type")
	}
}

func setObjVal(o interface{}) {
	v := reflect.ValueOf(o).Elem()
	k := v.Kind()
	switch k {
	case reflect.Int:
		v.SetInt(2)
	case reflect.Float32:
		v.SetFloat(2.2)
	case reflect.Float64:
		v.SetFloat(2.2)
	case reflect.String:
		v.SetString("str2")
	default:
		fmt.Println("other type")
	}
}

func PrintStructFields(o interface{}) {
	t := reflect.TypeOf(o)
	if t.Kind() != reflect.Struct && t.Elem().Kind() != reflect.Struct {
		panic("Illegal argument!")
		return
	}
	// 顺序与定义顺序一致
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		// tag 也可通过 field.Tag.Get(key string) 获取指定标签
		fmt.Printf("name:\t%v, type:\t%v, tag:\t%v\n", field.Name, field.Type, field.Tag)
	}
}

func PrintStructMethods(o interface{}) {
	t := reflect.TypeOf(o)
	if t.Kind() != reflect.Struct && t.Elem().Kind() != reflect.Struct {
		panic("Illegal argument!")
		return
	}
	// 顺序与方法名askii码顺序一致
	// 通过类型变量获取方法名称类型
	for i := 0; i < t.NumMethod(); i++ {
		method := t.Method(i)
		fmt.Printf("name:\t%v, type:\t%v\n", method.Name, method.Type)
	}
	// 通过值变量执行方法
	v := reflect.ValueOf(o)

	setMethod := v.MethodByName("SetInfo")
	var params []reflect.Value
	params = append(params, reflect.ValueOf("暗影精灵"))
	params = append(params, reflect.ValueOf(6666.6))
	setMethod.Call(params)

	printMethod := v.MethodByName("PrintInfo")
	printMethod.Call(nil)
}
